Refactoring EuiKeyPadMenuItem beta badge for improved accessibility#5508
Refactoring EuiKeyPadMenuItem beta badge for improved accessibility#55081Copenut wants to merge 8 commits intoelastic:mainfrom 1Copenut:feature/tpierce-eui-5290
EuiKeyPadMenuItem beta badge for improved accessibility#5508Conversation
* Moved the EuiBetaBadge outside the parent button * Updated the absolute positioning of the beta badge to match current * Added role="button" to beta badge span for more consistent SR read out
1Copenut
left a comment
There was a problem hiding this comment.
Added comments about two of the proposed changes.
| title={title || label} | ||
| > | ||
| <span tabIndex={0} className={classes} {...rest}> | ||
| <span tabIndex={0} className={classes} role="button" {...rest}> |
There was a problem hiding this comment.
I added this role="button" because Safari + VoiceOver wasn't consistently announcing the tooltip. Found this article Short note on aria-label, aria-labelledby, and aria-describedby by Leonie Watson that gave me a clue the span alone might not honor our ARIA markup consistently.
| } | ||
| } | ||
|
|
||
| .euiKeyPadMenuListItem { |
There was a problem hiding this comment.
Moved this out as a top-level element in the SCSS to ensure consistent absolute positioning and lessening risk of the cascade modifying something.
|
Preview documentation changes for this PR: https://eui.elastic.co/pr_5508/ |
|
|
||
| return ( | ||
| <EuiBetaBadge | ||
| aria-label={`${label}. Beta item.`} |
There was a problem hiding this comment.
Any hard coded text should use the useEuiI18n utility to allow localization.
There was a problem hiding this comment.
@cchaos does this labelling match the intent of specifying an iconType?
There was a problem hiding this comment.
My guess is that we should actually be labelling button as beta not the badge. And that we can’t hard-code “Beta item” at all, that the beta label needs to come from the betaBadgeLabel that the consumer supplies since it's not always going to mean "Beta" specifically.
@1Copenut I think we need to re-think this solution. Mainly, one of the issues I'm seeing is that if a consumer were to use EuiKeyPadMenuItem by itself, this beta badge would no longer be poisitioned against the button, but whatever next parent is position: relative.
Would it be a better solution to revert back the styling to include the beta badge within the button, but aria-hidden it and just apply the betaBadgeLabel as an extra aria-describedBy?
There was a problem hiding this comment.
Thank you @cchaos for this use case my solution hadn't anticipated. I'll give it another pass in a new branch with the approach you outlined and keep things in sync here so we don't lose the history.
I talked about this with Chandler in our 1:1 yesterday. Refactored it to add a relatively positioned DIV. I couldn't keep the beta badge nested inside the button because the axe plugin was complaining about a nested element, and hiding it with aria-hidden="true" would have removed the tooltip context for screen readers.
Moving the beta badge out to a sibling of the button felt like the best approach to balance all concerns.
EuiKeypadMenuItem beta badge for improved accessibilityEuiKeyPadMenuItem beta badge for improved accessibility
|
Preview documentation changes for this PR: https://eui.elastic.co/pr_5508/ |
| }, | ||
| className | ||
| ); | ||
| const betaItemLabel = useEuiI18n('euiKeyPadMenuItem.betaLabel', 'Beta item.'); |
There was a problem hiding this comment.
Sorry, I could've provided more guidance on the i18n util; including the label variable here will make the whole label localizable, instead of forcing the order.
| const betaItemLabel = useEuiI18n('euiKeyPadMenuItem.betaLabel', 'Beta item.'); | |
| const betaItemLabel = useEuiI18n( | |
| 'euiKeyPadMenuItem.betaLabel', | |
| '{label}. Beta item.', | |
| { label } | |
| ); |
and then,
aria-label={betaItemLabel}
There was a problem hiding this comment.
I'll grab this on Tuesday morning right away!
|
Preview documentation changes for this PR: https://eui.elastic.co/pr_5508/ |
| > | ||
| {renderContent()} | ||
| </Element> | ||
| <div className="euiKeyPadMenuItem__outer"> |
There was a problem hiding this comment.
After talking with @chandlerprall on 1/10, I added a containing DIV to manage relative positioning inside the EuiKeypadMenuItem component. This addresses @cchaos concern that we might lose that reference if the component is used outside the original context, and maintains the fix to support mouse, keyboard, and screen reader (semantic) accessibility.
|
Preview documentation changes for this PR: https://eui.elastic.co/pr_5508/ |
There was a problem hiding this comment.
Thanks for looking into it @1Copenut.
I think we'll need to do more than just wrap with a relative div and shift some actual styles around. Specifically I'm looking at things that consumers may want to customize like sizing. Here's an example in Kibana that will break with this implementation:
|
|
||
| return ( | ||
| <EuiBetaBadge | ||
| aria-label={betaItemLabel} |
There was a problem hiding this comment.
This beta label can't be hard-coded because it doesn't always actually mean "Beta".
| aria-label={betaItemLabel} | |
| aria-label={`${label} ${betaBadgeLabel}`} |
| > | ||
| {renderContent()} | ||
| </Element> | ||
| {betaBadgeLabel && !checkable && renderBetaBadge()} |
There was a problem hiding this comment.
The betaBadgeLabel check is already in renderBetaBadge() so I'd just move the !checkable check into there as well, so you can simplify this to just:
| {betaBadgeLabel && !checkable && renderBetaBadge()} | |
| {renderBetaBadge()} |
| const renderContent = () => ( | ||
| <span className="euiKeyPadMenuItem__inner"> | ||
| {checkable ? renderCheckableElement() : renderBetaBadge()} | ||
| {checkable && renderCheckableElement()} |
There was a problem hiding this comment.
The checkable check is already in renderCheckableElement()
| {checkable && renderCheckableElement()} | |
| {renderCheckableElement()} |
* Refactoring for variable container width. * Adding two spans to ensure positioning and variable width to container.
| > | ||
| {renderContent()} | ||
| </Element> | ||
| <span className="euiKeyPadMenuItem__outer"> |
There was a problem hiding this comment.
I thought a lot about @cchaos comments the past couple of days. After a number of false starts, I settled on this solution. The euiKeyPadMenuItem__outer class allows consumers to extend the component width easily. The euiKeyPadMenuItem__positioned class sets a relative position reference and a max-width so buttons, links, and labels maintain the 96px width. Beta badges will also maintain their position next to the button as separate focusable elements.
|
Preview documentation changes for this PR: https://eui.elastic.co/pr_5508/ |
|
Closing this PR for a more robust solution Caroline merged here #5541 |
Summary
The
EuiKeypadMenuItemhad a focusable span inside the focusable button, used to trigger the tooltips. Axe-core was complaining about this nested focusable element, so I refactored it to move it outside the button as a sibling element. Closes #5290.role="button"to beta badge span for more consistent SR read outaria-labelto the beta badgespan[role="button"]so it offers context to screen readersNot sure this qualifies as a breaking change but it might. The refactoring of HTML was limited to
EuiKeypadMenuItemin cases where the beta badge is present. Happy to flag it as a breaking change if reviewers feel it's warranted.Checklist
Props have proper autodocs and **[playground toggles](https://github.com/elastic/eui/blob/main/wiki/documentation-guidelines.md#adding-playground-toggles)**Added documentationChecked Code Sandbox works for any docs examples